#!/usr/bin/env python
# encoding: utf-8

import json
import urllib.request
import urllib.parse
import ssl

"""
use rest_cherrypy
"""


class SaltAPI(object):
    __token_id = ''

    def __init__(self, apiurl, username, password):
        self.__apiurl = apiurl.rstrip('/')
        self.__user = username
        self.__password = password

    def token_id(self):
        """
        用户登录并获取token id
        :return: 
        """
        ''' user login and get token id '''

        params = {'eauth': 'pam', 'username': self.__user, 'password': self.__password}
        # python2.x
        # encode = urllib.urlencode(params)
        # obj = urllib.unquote(encode)
        # python3.x
        encode = urllib.parse.urlencode(params)
        obj = urllib.parse.unquote(encode).encode(encoding='utf8')
        content = self.post_request(obj, prefix='/login')

        try:
            self.__token_id = content['return'][0]['token']
            # print(self.__token_id)
        except KeyError:
            raise KeyError

    def post_request(self, obj, prefix='/'):
        """
        
        :param obj: 
        :param prefix: 
        :return: 
        """
        url = self.__apiurl + prefix
        headers = {'X-Auth-Token': self.__token_id}
        # python2.x
        # req = urllib2.Request(url, obj, headers)
        # opener = urllib2.urlopen(req)
        # python3.x

        # 当目标网站使用的是自签名的证书时就会抛出一个错误
        # 1.使用ssl创建未经验证的上下文，在urlopen中传入上下文参数
        # context = ssl._create_unverified_context()
        # req = urllib.request.Request(url, obj, headers)
        # opener = urllib.request.urlopen(req, context=context)

        # 2.全局取消证书验证
        ssl._create_default_https_context = ssl._create_unverified_context
        req = urllib.request.Request(url, obj, headers)
        opener = urllib.request.urlopen(req)
        content = json.loads(opener.read().decode('utf8'))
        return content

    def list_all_key(self):
        """
        列出所有key
        :return: 
        """
        params = {'client': 'wheel', 'fun': 'key.list_all'}
        obj = urllib.parse.urlencode(params).encode(encoding='utf8')
        # encode = urllib.parse.urlencode(params)
        # obj = urllib.parse.unquote(encode).encode(encoding='utf8')
        self.token_id()
        content = self.post_request(obj)
        minions = content['return'][0]['data']['return']['minions']
        minions_pre = content['return'][0]['data']['return']['minions_pre']
        return minions, minions_pre

    def delete_key(self, node_name):
        """
        删除key
        :param node_name: 
        :return: 
        """
        params = {'client': 'wheel', 'fun': 'key.delete', 'match': node_name}
        obj = urllib.parse.urlencode(params).encode(encoding='utf8')
        self.token_id()
        content = self.post_request(obj)
        ret = content['return'][0]['data']['success']
        return ret

    def accept_key(self, node_name):
        """
        允许key
        :param node_name: 
        :return: 
        """
        params = {'client': 'wheel', 'fun': 'key.accept', 'match': node_name}
        obj = urllib.parse.urlencode(params).encode(encoding='utf8')
        self.token_id()
        content = self.post_request(obj)
        ret = content['return'][0]['data']['success']
        return ret

    def remote_noarg_execution(self, tgt, fun):
        """
        远程执行命令（不带参数）
        :param tgt: 
        :param fun: 
        :return: 
        """
        """ Execute commands without parameters """
        params = {'client': 'local', 'tgt': tgt, 'fun': fun}
        obj = urllib.parse.urlencode(params).encode(encoding='utf8')
        self.token_id()
        content = self.post_request(obj)
        ret = content['return'][0][tgt]
        return ret

    def remote_execution(self, tgt, fun, arg):
        """
        远程执行命令（带参数）
        :param tgt: 
        :param fun: 
        :param arg: 
        :return: 
        """
        params = {'client': 'local', 'tgt': tgt, 'fun': fun, 'arg': arg}
        obj = urllib.parse.urlencode(params).encode(encoding='utf8')
        self.token_id()
        content = self.post_request(obj)
        ret = content['return'][0][tgt]
        return ret

    def target_remote_execution(self, tgt, fun, arg):
        """ Use targeting for remote execution """
        params = {'client': 'local', 'tgt': tgt, 'fun': fun, 'arg': arg, 'expr_form': 'nodegroup'}
        obj = urllib.parse.urlencode(params).encode(encoding='utf8')
        self.token_id()
        content = self.post_request(obj)
        jid = content['return'][0]['jid']
        return jid

    def deploy(self, tgt, arg):
        """ Module deployment """
        params = {'client': 'local', 'tgt': tgt, 'fun': 'state.sls', 'arg': arg}
        obj = urllib.parse.urlencode(params).encode(encoding='utf8')
        self.token_id()
        content = self.post_request(obj)
        return content

    def async_deploy(self, tgt, arg):
        """ Asynchronously send a command to connected minions """
        params = {'client': 'local_async', 'tgt': tgt, 'fun': 'state.sls', 'arg': arg}
        obj = urllib.parse.urlencode(params).encode(encoding='utf8')
        self.token_id()
        content = self.post_request(obj)
        jid = content['return'][0]['jid']
        return jid

    def target_deploy(self, tgt, arg):
        """ Based on the node group forms deployment """
        params = {'client': 'local_async', 'tgt': tgt, 'fun': 'state.sls', 'arg': arg, 'expr_form': 'nodegroup'}
        obj = urllib.parse.urlencode(params).encode(encoding='utf8')
        self.token_id()
        content = self.post_request(obj)
        jid = content['return'][0]['jid']
        return jid


def main():
    salt_api = SaltAPI(apiurl='https://10.10.0.31:8000', username='saltapi', password='saltapi')
    # test ok
    # salt_api.token_id()

    # test ok
    # salt_api.delete_key('salt-node3')

    # test ok
    # salt_api.accept_key('salt-node3')

    # test ok
    # print(salt_api.list_all_key())

    # test ok
    # 类似于salt '*' test.ping
    # print(salt_api.remote_noarg_execution('10.10.0.31', 'grains.items'))
    # print(salt_api.remote_noarg_execution('10.10.0.31', 'test.ping'))

    # test ok
    # 类似于salt '*' cmd.run ifconfig
    # print(salt_api.remote_execution('10.10.0.31', 'cmd.run', 'ifconfig'))

    # 类似于salt '*' state.sls iftop
    # print(salt_api.deploy('10.10.0.31', 'iftop'))


if __name__ == '__main__':
    main()
